import tkinter as tk
from tkinter import filedialog, messagebox, simpledialog
import struct

class BBFS_Checksum_Calculator:
    def __init__(self, root):
        self.root = root
        self.root.title("BBFS 校验和计算工具  by sksaUltra64")
        self.root.geometry("800x550")
        
        # 尝试设置窗口透明度以实现毛玻璃效果
        try:
            self.root.attributes('-alpha', 0.95)
        except:
            pass
        
        # 文件路径变量
        self.file_path = tk.StringVar()
        
        # 创建界面元素
        self.create_widgets()
        
    def create_widgets(self):
        # 主框架
        main_frame = tk.Frame(self.root, bg='#F0F0F0', padx=15, pady=15)
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 文件选择部分
        file_frame = tk.Frame(main_frame, bg='#F0F0F0')
        file_frame.pack(fill=tk.X, pady=(0, 10))
        
        tk.Label(file_frame, text="BBFS 文件:", bg='#F0F0F0').pack(side=tk.LEFT)
        tk.Entry(file_frame, textvariable=self.file_path, width=50).pack(side=tk.LEFT, padx=5)
        
        # 按钮容器
        button_container = tk.Frame(file_frame, bg='#F0F0F0')
        button_container.pack(side=tk.LEFT, padx=(10, 0))
        
        tk.Button(button_container, text="浏览", command=self.browse_file, width=6).pack(side=tk.LEFT, padx=2)
        tk.Button(button_container, text="计算校验和", command=self.calculate_checksum, width=10).pack(side=tk.LEFT, padx=2)
        tk.Button(button_container, text="验证校验和", command=self.verify_checksum, width=10).pack(side=tk.LEFT, padx=2)
        tk.Button(button_container, text="清除", command=self.clear_text, width=6).pack(side=tk.LEFT, padx=2)
        
        # 坏块操作部分
        badblock_frame = tk.Frame(main_frame, bg='#F0F0F0')
        badblock_frame.pack(fill=tk.X, pady=(0, 10))
        
        tk.Button(badblock_frame, text="标记坏块", command=self.mark_bad_block, width=10).pack(side=tk.LEFT, padx=2)
        tk.Button(badblock_frame, text="修复校验和", command=self.fix_checksum, width=10).pack(side=tk.LEFT, padx=2)
        tk.Button(badblock_frame, text="查看块状态", command=self.view_block_status, width=10).pack(side=tk.LEFT, padx=2)
        
        # 信息显示部分
        info_frame = tk.LabelFrame(main_frame, text="文件信息", font=("Arial", 10, "bold"), bg='#F0F0F0')
        info_frame.pack(fill=tk.BOTH, expand=True, pady=10)
        
        self.info_text = tk.Text(info_frame, height=15, width=80, wrap=tk.WORD)
        scrollbar = tk.Scrollbar(info_frame, orient=tk.VERTICAL, command=self.info_text.yview)
        self.info_text.configure(yscrollcommand=scrollbar.set)
        
        self.info_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y, pady=5)
        
    def browse_file(self):
        filename = filedialog.askopenfilename(
            title="选择 BBFS 块文件",
            filetypes=(("二进制文件", "*.bin"), ("所有文件", "*.*"))
        )
        if filename:
            self.file_path.set(filename)
            self.display_file_info(filename)
    
    def display_file_info(self, filename):
        try:
            with open(filename, 'rb') as f:
                data = f.read()
                
            if len(data) != 16384:
                self.info_text.insert(tk.END, f"警告: 文件大小不是 16384 字节 (实际: {len(data)} 字节)\n")
                return
                
            # 读取文件尾信息
            footer = data[0x3FF0:0x4000]
            magic = footer[4:8]
            sequence = struct.unpack('>I', footer[8:12])[0]
            link_block = struct.unpack('>H', footer[12:14])[0]
            checksum = struct.unpack('>H', footer[14:16])[0]
            
            self.info_text.insert(tk.END, f"文件: {filename}\n")
            self.info_text.insert(tk.END, f"文件大小: {len(data)} 字节\n")
            self.info_text.insert(tk.END, f"Magic: {magic.decode('ascii', errors='replace')}\n")
            self.info_text.insert(tk.END, f"序列号: 0x{sequence:04X} ({sequence})\n")
            self.info_text.insert(tk.END, f"链接块: 0x{link_block:04X}\n")
            self.info_text.insert(tk.END, f"存储的校验和: 0x{checksum:04X}\n")
            
        except Exception as e:
            messagebox.showerror("错误", f"读取文件时出错: {str(e)}")
    
    def calculate_checksum(self):
        if not self.file_path.get():
            messagebox.showwarning("警告", "请先选择 BBFS 文件")
            return
            
        try:
            with open(self.file_path.get(), 'rb') as f:
                data = f.read()
                
            if len(data) != 16384:
                messagebox.showwarning("警告", "文件大小不是 16384 字节")
                return
                
            # 计算校验和
            total = 0
            for i in range(0, 0x3FFE, 2):
                value = struct.unpack('>H', data[i:i+2])[0]
                total = (total + value) & 0xFFFF
                
            checksum = (0xCAD7 - total) & 0xFFFF
            
            self.info_text.insert(tk.END, f"计算出的校验和: 0x{checksum:04X}\n")
            
        except Exception as e:
            messagebox.showerror("错误", f"计算校验和时出错: {str(e)}")
    
    def verify_checksum(self):
        if not self.file_path.get():
            messagebox.showwarning("警告", "请先选择 BBFS 文件")
            return
            
        try:
            with open(self.file_path.get(), 'rb') as f:
                data = f.read()
                
            if len(data) != 16384:
                messagebox.showwarning("警告", "文件大小不是 16384 字节")
                return
                
            # 读取存储的校验和
            stored_checksum = struct.unpack('>H', data[0x3FFE:0x4000])[0]
            
            # 计算校验和
            total = 0
            for i in range(0, 0x4000, 2):
                value = struct.unpack('>H', data[i:i+2])[0]
                total = (total + value) & 0xFFFF
                
            is_valid = total == 0xCAD7
            
            self.info_text.insert(tk.END, f"存储的校验和: 0x{stored_checksum:04X}\n")
            self.info_text.insert(tk.END, f"验证结果: {'有效' if is_valid else '无效'}\n")
            
            if not is_valid:
                # 计算正确的校验和
                total_without_checksum = 0
                for i in range(0, 0x3FFE, 2):
                    value = struct.unpack('>H', data[i:i+2])[0]
                    total_without_checksum = (total_without_checksum + value) & 0xFFFF
                    
                correct_checksum = (0xCAD7 - total_without_checksum) & 0xFFFF
                self.info_text.insert(tk.END, f"正确的校验和应为: 0x{correct_checksum:04X}\n")
            
        except Exception as e:
            messagebox.showerror("错误", f"验证校验和时出错: {str(e)}")
    
    def mark_bad_block(self):
        if not self.file_path.get():
            messagebox.showwarning("警告", "请先选择 BBFS 文件")
            return
            
        try:
            # 获取坏块号
            block_num = simpledialog.askinteger("标记坏块", "请输入坏块号(0-4095):", 
                                              parent=self.root, minvalue=0, maxvalue=4095)
            if block_num is None:
                return
                
            # 确认对话框
            if not messagebox.askyesno("确认", "标记坏块可能会修改文件数据。确定要继续吗？"):
                return
                
            with open(self.file_path.get(), 'r+b') as f:
                # 计算坏块状态在文件中的偏移
                block_offset = block_num * 2
                
                # 移动到坏块状态位置
                f.seek(block_offset)
                
                # 读取当前状态
                current_value = f.read(2)
                
                # 标记为坏块
                f.seek(block_offset)
                f.write(b'\xFF\xFE')
                
                self.info_text.insert(tk.END, f"已标记块 {block_num} 为坏块 (文件内偏移: 0x{block_offset:04X})\n")
                self.info_text.insert(tk.END, f"原来的状态: {current_value.hex().upper()}\n")
                self.info_text.insert(tk.END, "注意: 标记坏块后，请重新修复校验和！\n")
                    
        except Exception as e:
            messagebox.showerror("错误", f"标记坏块时出错: {str(e)}")
    
    def view_block_status(self):
        if not self.file_path.get():
            messagebox.showwarning("警告", "请先选择 BBFS 文件")
            return
            
        try:
            # 获取块号
            block_num = simpledialog.askinteger("查看块状态", "请输入块号(0-4095):", 
                                              parent=self.root, minvalue=0, maxvalue=4095)
            if block_num is None:
                return
                
            with open(self.file_path.get(), 'rb') as f:
                # 计算块状态在文件中的偏移
                block_offset = block_num * 2
                
                # 移动到块状态位置
                f.seek(block_offset)
                
                # 读取块状态
                block_status = f.read(2)
                
                # 显示块状态
                self.info_text.insert(tk.END, f"块 {block_num} 的状态 (偏移: 0x{block_offset:04X}):\n")
                self.info_text.insert(tk.END, f"十六进制: {block_status.hex().upper()}\n")
                
                # 解释状态
                if block_status == b'\xFF\xFD':
                    status_desc = "好块"
                elif block_status == b'\xFF\xFE':
                    status_desc = "坏块"
                else:
                    status_desc = "未知状态"
                
                self.info_text.insert(tk.END, f"状态: {status_desc}\n")
                    
        except Exception as e:
            messagebox.showerror("错误", f"查看块状态时出错: {str(e)}")
    
    def fix_checksum(self):
        if not self.file_path.get():
            messagebox.showwarning("警告", "请先选择 BBFS 文件")
            return
            
        try:
            with open(self.file_path.get(), 'r+b') as f:
                data = f.read()
                
            if len(data) != 16384:
                messagebox.showwarning("警告", "文件大小不是 16384 字节")
                return
                
            # 计算校验和
            total = 0
            for i in range(0, 0x3FFE, 2):
                value = struct.unpack('>H', data[i:i+2])[0]
                total = (total + value) & 0xFFFF
                
            checksum = (0xCAD7 - total) & 0xFFFF
            
            # 写入校验和
            with open(self.file_path.get(), 'r+b') as f:
                f.seek(0x3FFE)
                f.write(struct.pack('>H', checksum))
                
            self.info_text.insert(tk.END, f"已修复校验和: 0x{checksum:04X}\n")
            self.info_text.insert(tk.END, "请重新验证校验和以确保修复成功\n")
            
        except Exception as e:
            messagebox.showerror("错误", f"修复校验和时出错: {str(e)}")
    
    def clear_text(self):
        self.info_text.delete(1.0, tk.END)

if __name__ == "__main__":
    root = tk.Tk()
    app = BBFS_Checksum_Calculator(root)
    root.mainloop()